//参考
//https://www.runoob.com/cprogramming/c-arrays.html
//https://blog.csdn.net/qq_15144867/article/details/93341737
//https://blog.csdn.net/zqixiao_09/article/details/50877383

#include "ls.h"

#define MASKS_LEN 10
#define SEP "\t"

//视情况采用stat或lstat
int (*stat_f)(const char*, struct stat*) = stat;

const char ERR_CHAR = ' ';

char fileType(mode_t mode) {
	switch (mode & S_IFMT) {
	case S_IFREG:
		return '-';
	case S_IFDIR:
		return 'd';
	case S_IFCHR:
		return 'c';
	case S_IFBLK:
		return 'b';
	case S_IFIFO:
		return 'f';
	case S_IFLNK:
		return 'l';
	case S_IFSOCK:
		return 's';
	default:
		//unknown
		return ERR_CHAR;
	}
}

char filePermission(mode_t mode, mode_t mask) {
	if (mode & mask == 0)
		return '-';
	else {
		switch (mask) {
		case S_IRUSR:
		case S_IRGRP:
		case S_IROTH:
			return 'r';
		case S_IWUSR:
		case S_IWGRP:
		case S_IWOTH:
			return 'w';
		case S_IXUSR:
		case S_IXGRP:
		case S_IXOTH:
			return 'x';
		default:
			return ERR_CHAR;
		}
	}
}

const size_t MODE_STR_LEN = 12;
const mode_t MASKS[MASKS_LEN] = { 0,
//用户（所有者）
		S_IRUSR, S_IWUSR, S_IXUSR,
		//组
		S_IRGRP, S_IWGRP, S_IXGRP,
		//其他
		S_IROTH, S_IWOTH, S_IXOTH };
const char PERMISSION_STR[] = "Erwxrwxrwx";
void filePermission2(char *modeStr, mode_t mode, size_t idx);
inline void filePermission2(char *modeStr, mode_t mode, size_t idx) {
	modeStr[idx] = (mode & MASKS[idx]) ? PERMISSION_STR[idx] : '-';
}
void outputFileMode(mode_t mode) {
	char modeStr[MODE_STR_LEN] /*  = {'\0'} */;
	modeStr[0] = fileType(mode);
	size_t i;
	for (i = 1; i < MASKS_LEN; ++i) {
		//modeStr[i] = filePermission(mode, MASKS[i]);
		filePermission2(modeStr, mode, i);
	}
	modeStr[MASKS_LEN] = '\0';
	printf("%s"SEP, modeStr);
}

void print_type(mode_t mode) {
	printf("%c", fileType(mode));
}
void print_perm(mode_t mode) {
	char modeStr[MODE_STR_LEN];
	size_t i;
	for (i = 1; i < MASKS_LEN; ++i)
		filePermission2(modeStr, mode, i);
	modeStr[MASKS_LEN] = '\0';
	printf("%s"SEP, modeStr + 1);
}

void print_link(unsigned long link) {
	printf("%lu"SEP, link);
}

void print_size(long size) {
	printf("%ld"SEP, size);
}

void print_time(time_t t) {
	char *s = ctime(&t);
	//abort();
	char *s2 = strchr(s, '\n');
	if (s2 != NULL)
		*s2 = '\0';
	printf("%s"SEP, s);
}

void print_filename(const struct dirent *curdp) {
	printf("%s\n", curdp->d_name);
}

void print_usrname(uid_t uid) {
	printf("%s"SEP, getpwuid(uid)->pw_name);
}

void print_grname(gid_t gid) {
	printf("%s"SEP, getgrgid(gid)->gr_name);
}

//文件类型 权限 硬链接数 所有者用户名 所有者所在组用户名 文件大小 最后修改时间
void ls(char dirPath[]) {
	DIR *catalog = opendir(dirPath);
	if (catalog == NULL) {
		printf("open directory failed\n");
		perror(dirPath);
	} else {
		//目录项
		struct dirent *currentdp;
		for (currentdp = readdir(catalog); currentdp != NULL; currentdp =
				readdir(catalog)) {
			if ((strcmp(currentdp->d_name,".")!=0)&&(strcmp(currentdp->d_name,"..")!=0)) {
				struct stat currentstat;
				char dirPath_copy[PATH_MAX];
				memset(dirPath_copy, '\0', sizeof(dirPath_copy));
				strcpy(dirPath_copy, dirPath);
				strcat(strcat(dirPath_copy, "/"), currentdp->d_name);
				if (stat_f(dirPath_copy, &currentstat) != 0) {
					//出错
					printf("get stat error\n");
					perror(dirPath_copy);
					//无需释放
					//free(currentdp);
					//break;
					continue;
				} else {
					//输出文件（夹）属性
					//outputFileMode(currentstat.st_mode);
					if (fileType(currentstat.st_mode) != 'd') {
						pid_t pid_child, pid_return;
						pid_child = fork();
						if (pid_child < 0)
							printf("Error occured on forking.\n");
						else if (pid_child == 0) {
							//运行mycp以复制
							execl("./mycp", "./mycp", dirPath_copy,
									"/home/lihuantong/tmp", NULL);
						}
						do {
							pid_return = waitpid(pid_child, NULL, WNOHANG);
						} while (pid_return == 0);
						if (pid_return != pid_child) {
							printf("1some error occured\n");
						}
					} else if (fileType(currentstat.st_mode) == 'd') {
						pid_t pid_child, pid_return;
						pid_child = fork();
						if (pid_child < 0)
							printf("Error occured on forking.\n");
						else if (pid_child == 0) {
							//运行myls以继续查找
							execl("./myls", "./myls", dirPath_copy, NULL);
						}
						do {
							pid_return = waitpid(pid_child, NULL, WNOHANG);
						} while (pid_return == 0);
						if (pid_return != pid_child) {
							printf("2some error occured\n");
						}
					}
				}
			}
		}
	}
	closedir(catalog);
}
